# -*- coding: utf-8 -*-
# @Time    : 2024/12/07 14:00
# @Author  : CHNJX
# @File    : log_analysis.py
# @Desc    : 日志分析API

import os
from flask import Blueprint, request, send_file, current_app
from werkzeug.utils import secure_filename
from app.services.log_analysis_service import LogAnalysisService
from app.utils.response_handler import response_with_handler, success_response, error_response

log_analysis_bp = Blueprint('log_analysis', __name__)


@log_analysis_bp.route('/upload', methods=['POST'])
@response_with_handler
def upload_log_file():
    """上传并分析日志文件"""
    # 添加详细的调试信息
    current_app.logger.info(f"上传请求详情:")
    current_app.logger.info(f"  Content-Type: {request.content_type}")
    current_app.logger.info(f"  Files: {dict(request.files)}")
    current_app.logger.info(f"  Form: {dict(request.form)}")
    current_app.logger.info(f"  Args: {dict(request.args)}")
    
    if 'file' not in request.files:
        current_app.logger.error("请求中未找到 'file' 字段")
        return error_response("未找到上传的文件", code=400)
    
    file = request.files['file']
    current_app.logger.info(f"文件对象详情:")
    current_app.logger.info(f"  filename: {file.filename}")
    current_app.logger.info(f"  content_type: {file.content_type}")
    current_app.logger.info(f"  content_length: {file.content_length}")
    
    if file.filename == '' or file.filename is None:
        current_app.logger.error("文件名为空")
        return error_response("未选择文件", code=400)
    
    # 检查文件是否为空
    file.seek(0, 2)  # 移动到文件末尾
    file_size = file.tell()
    file.seek(0)  # 重置到文件开头
    
    current_app.logger.info(f"文件大小: {file_size} 字节")
    
    if file_size == 0:
        current_app.logger.error("上传的文件为空")
        return error_response("上传的文件为空", code=400)
    
    # 检查文件类型
    allowed_extensions = {'.log', '.txt'}
    if file.filename:
        file_ext = os.path.splitext(file.filename)[1].lower()
        if file_ext not in allowed_extensions:
            return error_response(f"不支持的文件类型，仅支持: {', '.join(allowed_extensions)}", code=400)
    
    try:
        # 初始化服务
        service = LogAnalysisService()
        
        # 保存上传的文件
        log_file_path = service.save_uploaded_file(file)
        current_app.logger.info(f"文件已保存到: {log_file_path}")
        
        # 分析日志文件
        analysis_results, txt_path = service.analyze_log_file(log_file_path)
        
        # 删除原始日志文件
        service.delete_file(log_file_path)
        
        # 构建响应数据
        response_data = {
            'analysis_results': analysis_results,
            'result_file': os.path.basename(txt_path) if txt_path else None
        }
        
        return success_response(response_data, "日志文件分析完成")
        
    except Exception as e:
        current_app.logger.error(f"分析失败: {str(e)}")
        # 如果分析失败，尝试清理上传的文件
        if 'log_file_path' in locals():
            try:
                service.delete_file(log_file_path)
            except:
                pass
        return error_response(f"分析失败: {str(e)}", code=500)


@log_analysis_bp.route('/results', methods=['GET'])
@response_with_handler
def list_result_files():
    """获取所有分析结果文件列表"""
    try:
        service = LogAnalysisService()
        files = service.list_result_files()
        return success_response(files, "获取结果文件列表成功")
    except Exception as e:
        return error_response(f"获取文件列表失败: {str(e)}", code=500)


@log_analysis_bp.route('/download/<filename>', methods=['GET'])
def download_result_file(filename):
    """下载分析结果文件"""
    try:
        service = LogAnalysisService()
        
        # 首先检查原始文件名是否存在
        file_path = os.path.join(service.result_dir, filename)
        file_path = os.path.abspath(file_path)
        result_dir = os.path.abspath(service.result_dir)
        
        # 检查文件是否在结果目录内（防止路径遍历攻击）
        if not file_path.startswith(result_dir + os.sep) and file_path != result_dir:
            from flask import jsonify
            return jsonify(error_response("非法文件访问", code=403)), 403
        
        if not os.path.exists(file_path):
            from flask import jsonify
            return jsonify(error_response("文件不存在", code=404)), 404
        
        # 确定下载时的文件名（处理中文字符问题）
        safe_filename = secure_filename(filename)
        if not safe_filename:  # 如果secure_filename返回空（比如全是中文），使用原文件名
            safe_filename = filename
        
        return send_file(
            file_path,
            as_attachment=True,
            download_name=safe_filename,
            mimetype='application/octet-stream'
        )
        
    except Exception as e:
        current_app.logger.error(f"下载文件失败: {str(e)}")
        from flask import jsonify
        return jsonify(error_response(f"下载失败: {str(e)}", code=500)), 500


@log_analysis_bp.route('/delete/<filename>', methods=['DELETE'])
@response_with_handler
def delete_result_file(filename):
    """删除指定的分析结果文件"""
    try:
        service = LogAnalysisService()
        
        # 直接使用文件名，不进行secure_filename检查
        file_path = os.path.join(service.result_dir, filename)
        file_path = os.path.abspath(file_path)
        result_dir = os.path.abspath(service.result_dir)
        
        # 检查文件是否在结果目录内（防止路径遍历攻击）
        if not file_path.startswith(result_dir + os.sep) and file_path != result_dir:
            return error_response("非法文件访问", code=403)
        
        if service.delete_file(file_path):
            return success_response(message=f"文件删除成功")
        else:
            return error_response("文件删除失败或文件不存在", code=404)
            
    except Exception as e:
        return error_response(f"删除失败: {str(e)}", code=500)


@log_analysis_bp.route('/delete-batch', methods=['DELETE'])
@response_with_handler
def delete_batch_files():
    """批量删除分析结果文件"""
    data = request.get_json()
    if not data or 'filenames' not in data:
        return error_response("缺少文件名列表参数", code=400)
    
    filenames = data['filenames']
    if not isinstance(filenames, list):
        return error_response("文件名参数必须是数组格式", code=400)
    
    try:
        service = LogAnalysisService()
        deleted_files = []
        failed_files = []
        result_dir = os.path.abspath(service.result_dir)
        
        for filename in filenames:
            # 直接使用文件名，不进行secure_filename检查
            file_path = os.path.join(service.result_dir, filename)
            file_path = os.path.abspath(file_path)
            
            # 检查文件是否在结果目录内
            if not file_path.startswith(result_dir + os.sep) and file_path != result_dir:
                failed_files.append({'filename': filename, 'reason': '非法文件访问'})
                continue
            
            if service.delete_file(file_path):
                deleted_files.append(filename)
            else:
                failed_files.append({'filename': filename, 'reason': '文件不存在或删除失败'})
        
        response_data = {
            'deleted_files': deleted_files,
            'failed_files': failed_files,
            'total_requested': len(filenames),
            'deleted_count': len(deleted_files),
            'failed_count': len(failed_files)
        }
        
        if failed_files:
            return success_response(
                response_data, 
                f"批量删除完成，成功删除 {len(deleted_files)} 个文件，{len(failed_files)} 个文件删除失败"
            )
        else:
            return success_response(response_data, f"批量删除成功，共删除 {len(deleted_files)} 个文件")
            
    except Exception as e:
        return error_response(f"批量删除失败: {str(e)}", code=500)


@log_analysis_bp.route('/analyze-only', methods=['POST'])
@response_with_handler
def analyze_only():
    """仅分析日志文件，不删除原文件（用于调试）"""
    if 'file' not in request.files:
        return error_response("未找到上传的文件", code=400)
    
    file = request.files['file']
    if file.filename == '':
        return error_response("未选择文件", code=400)
    
    # 检查文件类型
    allowed_extensions = {'.log', '.txt'}
    if file.filename:
        file_ext = os.path.splitext(file.filename)[1].lower()
        if file_ext not in allowed_extensions:
            return error_response(f"不支持的文件类型，仅支持: {', '.join(allowed_extensions)}", code=400)
    
    try:
        # 初始化服务
        service = LogAnalysisService()
        
        # 保存上传的文件
        log_file_path = service.save_uploaded_file(file)
        
        # 分析日志文件
        analysis_results, txt_path = service.analyze_log_file(log_file_path)
        
        # 构建响应数据
        response_data = {
            'analysis_results': analysis_results,
            'uploaded_file': os.path.basename(log_file_path),
            'result_file': os.path.basename(txt_path) if txt_path else None
        }
        
        return success_response(response_data, "日志文件分析完成（原文件已保留）")
        
    except Exception as e:
        return error_response(f"分析失败: {str(e)}", code=500)


@log_analysis_bp.route('/health', methods=['GET'])
@response_with_handler
def health_check():
    """健康检查接口"""
    try:
        service = LogAnalysisService()
        
        # 检查目录是否可访问
        upload_dir_accessible = os.path.exists(service.upload_dir) and os.access(service.upload_dir, os.W_OK)
        result_dir_accessible = os.path.exists(service.result_dir) and os.access(service.result_dir, os.W_OK)
        
        health_data = {
            'service_status': 'healthy',
            'upload_directory': {
                'path': service.upload_dir,
                'accessible': upload_dir_accessible
            },
            'result_directory': {
                'path': service.result_dir,
                'accessible': result_dir_accessible
            },
            'result_files_count': len(service.list_result_files())
        }
        
        if upload_dir_accessible and result_dir_accessible:
            return success_response(health_data, "日志分析服务运行正常")
        else:
            return error_response("服务配置异常", code=500, data=health_data)
            
    except Exception as e:
        return error_response(f"健康检查失败: {str(e)}", code=500)


@log_analysis_bp.route('/debug-upload', methods=['POST'])
@response_with_handler
def debug_upload():
    """调试上传接口，查看前端发送的数据格式"""
    debug_info = {
        'request_info': {
            'content_type': request.content_type,
            'content_length': request.content_length,
            'method': request.method,
        },
        'headers': dict(request.headers),
        'files': {},
        'form': dict(request.form),
        'json': None,
        'raw_data': None,
        'args': dict(request.args)
    }
    
    # 详细分析文件对象
    if request.files:
        for key, file_obj in request.files.items():
            # 获取文件大小
            file_obj.seek(0, 2)  # 移动到末尾
            file_size = file_obj.tell()
            file_obj.seek(0)  # 重置到开头
            
            debug_info['files'][key] = {
                'filename': file_obj.filename,
                'content_type': file_obj.content_type,
                'content_length': file_obj.content_length,
                'file_size': file_size,
                'headers': dict(file_obj.headers) if hasattr(file_obj, 'headers') else None,
                'is_empty': file_size == 0,
                'has_filename': bool(file_obj.filename),
            }
            
            # 如果文件不为空，读取前100个字符作为预览
            if file_size > 0:
                preview_data = file_obj.read(100)
                file_obj.seek(0)  # 重置
                try:
                    debug_info['files'][key]['preview'] = preview_data.decode('utf-8', errors='ignore')
                except:
                    debug_info['files'][key]['preview'] = f"二进制数据，前{len(preview_data)}字节"
    
    # 尝试解析JSON
    try:
        debug_info['json'] = request.get_json()
    except:
        pass
    
    # 获取原始数据
    try:
        if request.data:
            debug_info['raw_data'] = request.data.decode('utf-8', errors='ignore')[:500]  # 限制长度
    except:
        debug_info['raw_data'] = "无法解码原始数据"
    
    current_app.logger.info(f"调试上传接口接收到的数据: {debug_info}")
    
    return success_response(debug_info, "调试信息获取成功")


@log_analysis_bp.route('/upload-by-reference', methods=['POST'])
@response_with_handler
def upload_by_reference():
    """通过文件引用上传并分析日志文件（用于前端文件组件）"""
    data = request.get_json()
    
    if not data:
        return error_response("请求数据为空", code=400)
    
    current_app.logger.info(f"接收到的JSON数据: {data}")
    
    # 检查是否包含文件信息
    file_info = None
    if isinstance(data, dict):
        if 'file' in data:
            file_info = data['file']
        elif 'uid' in data:
            # 直接是文件信息对象
            file_info = data
    
    if not file_info:
        return error_response("未找到文件信息", code=400)
    
    # 提取文件的uid
    uid = file_info.get('uid')
    if not uid:
        return error_response("未找到文件唯一标识", code=400)
    
    # 这里需要根据你的前端框架来获取实际文件
    # 通常前端会提供一个API来通过uid获取文件内容
    return error_response("需要前端提供文件内容获取机制", code=501) 